package com.crack.spansframe;

import android.content.Context;
import android.graphics.Color;
import android.graphics.Typeface;
import android.graphics.drawable.Drawable;
import android.text.Layout;
import android.text.Spannable;
import android.text.SpannableStringBuilder;
import android.text.Spanned;
import android.text.style.AlignmentSpan;
import android.text.style.ForegroundColorSpan;
import android.text.style.RelativeSizeSpan;
import android.text.style.StrikethroughSpan;
import android.text.style.StyleSpan;
import android.text.style.UnderlineSpan;
import android.widget.TextView;

import com.crack.spansframe.extra.ICache;
import com.crack.spansframe.extra.INeedTextView;

import java.util.Arrays;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 解析
 * Created by liuyu on 16-4-12.
 */
@SuppressWarnings("all")
public class SpanSolve {

    public static Drawable sQuoteDrawable;
    public static Drawable sLoadingDrawable;
    public static Drawable sCollDrawable;
    public static float sCollRatio;

    /**
     * 注入textView，在setText之前调用
     *
     * @param spannable
     * @param textView
     */
    public static void injectTextView(Spannable spannable, TextView textView) {
        INeedTextView[] spans = spannable.getSpans(0, spannable.length(), INeedTextView.class);
        for (INeedTextView span : spans) {
            span.setTextView(textView);
        }
    }

    public static void injectContext(Context context) {
        sQuoteDrawable = context.getResources().getDrawable(R.drawable.reply_bg);
        sLoadingDrawable = context.getResources().getDrawable(R.drawable.loading_img);
        sCollDrawable = context.getResources().getDrawable(R.drawable.collapsed_no);
        sCollRatio = sCollDrawable.getIntrinsicWidth() * 1.0f / sCollDrawable.getIntrinsicHeight();
    }

    String source;
    SpanSet set;
    public AsyncLoadCallBack callBack;

    /**
     * {@link MarginQuoteSpan#LINE_OFFSET}
     *
     * @param string
     */
    public SpanSolve(String string) {
        this.source = string;
        source = source.replaceAll("<br/>", "\n");
        source = source.replaceAll("\\[quote.*?\\]", "\n$0");   //美化布局,配合MarginQuoteSpan
        source = source.replaceAll("\\[/quote\\]", "$0\n");     //美化布局,配合MarginQuoteSpan
        set = new SpanSet();
    }

    public SpannableStringBuilder resolve() {
        findQuote();
        findColor();
        findBIUD();
        findSize();
        findList();
        findAlign();
        findImg();
        findLine();
        findUrl();
        SpannableStringBuilder sp = setSpan(set);
        findCollapse(sp);
        return sp;
    }

    private void findCollapse(SpannableStringBuilder sp) {
        Pattern p = Pattern.compile("\\[collapse=(.*?)\\](.*?)\\[/collapse\\]", Pattern.DOTALL);
        Matcher m = p.matcher(sp.toString());
        if (m.find()) {
            SpannableStringBuilder coll = (SpannableStringBuilder) sp.subSequence(m.start(1), m.end(1));
            SpannableStringBuilder content = (SpannableStringBuilder) sp.subSequence(m.start(2), m.end(2));
            if (content.toString().matches("\\[collapse(=.?)?\\]")) {
                return;
            }
            coll.setSpan(new CollSpan(sp, content), 0, coll.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
            sp.replace(m.start(0), m.end(0), coll);
            findCollapse(sp);
        }

//        int gap = 0;
//        while (m.find()) {
//            sb.append(source, gap, m.start(0));
//            String coll = m.group(1);
//            String content = m.group(2);
//            int start = sb.length();
//            sb.append(coll);
//            int end = sb.length();
//            set.offset(start, m.start(1) - m.start(0));
//            set.offset(end, m.end(0) - m.start(2));
//            set.put(start,end,SpanSet.COLL);
//            gap = m.end(0);
//        }
//        sb.append(source, gap, source.length());
//        source = sb.toString();
    }

    private void findUrl() {
        Matcher m = Pattern.compile("\\[url=(.*?)\\](.*?)\\[/url\\]").matcher(source);
        StringBuilder sb = new StringBuilder(source.length());
        int gap = 0;
        while (m.find()) {
            sb.append(source, gap, m.start(0));
            String url = m.group(1);
            int start = sb.length();
            set.offset(start, m.start(2) - m.start(0));
            sb.append(m.group(2));
            int end = sb.length();
            set.offset(end, m.end(0) - m.end(2));
            set.put(start, end, SpanSet.URL, url);
            gap = m.end(0);
        }
        sb.append(source, gap, source.length());
        source = sb.toString();
    }

    private void findLine() {
        Pattern p = Pattern.compile("={3,}\n");
        Matcher m = p.matcher(source);
        StringBuilder sb = new StringBuilder(source);
        while (m.find()) {
            int start = m.start();
            int end = m.end() - 1;
            int len = end - start;
            char[] chs = new char[len];
            Arrays.fill(chs, ' ');
            sb.replace(start, end, new String(chs));
            set.put(start, end, SpanSet.LINE);
        }
        source = sb.toString();
    }

    private void findImg() {
        StringBuilder sb = new StringBuilder(source.length());
        Pattern p = Pattern.compile("\\[img\\](.*?)\\[/img\\]", Pattern.DOTALL);
        Matcher m = p.matcher(source);
        int gap = 0;
        while (m.find()) {
            String img = m.group(1);
            sb.append(source, gap, m.start(0));
            int start = sb.length();
            int leftLen = m.start(1) - m.start(0);
            sb.append(m.group(1));
            int end = sb.length();
            int rightLen = m.end(0) - m.end(1);
            set.offset(start, leftLen);
            set.offset(end, rightLen);
            set.put(start, end, SpanSet.IMAGE, img);
            gap = m.end(0);
        }
        sb.append(source, gap, source.length());
        source = sb.toString();
    }

    private void findAlign() {
        StringBuilder sb = new StringBuilder(source.length());
        Pattern p = Pattern.compile("\\[align=(.*?)\\](.*?)\\[/align\\]", Pattern.DOTALL);
        Matcher m = p.matcher(source);
        int gap = 0;
        while (m.find()) {
            String align = m.group(1);
            sb.append(source, gap, m.start(0));
            int start = sb.length();
            int leftLen = m.start(2) - m.start(0);
            sb.append(m.group(2));
            int end = sb.length();
            int rightLen = m.end(0) - m.end(2);
            set.offset(start, leftLen);
            set.offset(end, rightLen);
            set.put(start, end, SpanSet.ALIGN, align);
            gap = m.end(0);
        }
        sb.append(source, gap, source.length());
        source = sb.toString();
    }

    private void findList() {
        StringBuilder sb = new StringBuilder(source.length());
        Pattern p = Pattern.compile("\\[list\\](.*?)\\[/list\\]", Pattern.DOTALL);
        Matcher m = p.matcher(source);
        int gap = 0;
        while (m.find()) {
            sb.append(source, gap, m.start(0));
            int start = sb.length();
            String list = m.group(1);
            set.offset(start, "[list]".length());
            Pattern pp = Pattern.compile("\\[\\*\\](.*?\n)", Pattern.DOTALL);
            Matcher mm = pp.matcher(list);
            int gp = 0;
            while (mm.find()) {
                sb.append(list, gp, mm.start(0));
                int st = sb.length();
                set.offset(st, "[*]".length());
                String text = mm.group(1);
                sb.append(text);
                int ed = sb.length();
                set.put(st, ed - 1, SpanSet.LIST);
                gp = mm.end(0);
            }
            sb.append(list, gp, list.length());
            int end = sb.length();
            set.offset(end, "[/list]".length());
            gap = m.end(0);
        }
        sb.append(source, gap, source.length());
        source = sb.toString();
    }

    private void findSize() {
        StringBuilder sb = new StringBuilder(source.length());
        Pattern p = Pattern.compile("\\[size=(\\d+?)%\\](.*?)\\[/size\\]", Pattern.DOTALL);
        Matcher m = p.matcher(source);
        int gap = 0;
        while (m.find()) {
            String size = m.group(1);
            sb.append(source, gap, m.start(0));
            int start = sb.length();
            int leftLen = m.start(2) - m.start(0);
            sb.append(m.group(2));
            int end = sb.length();
            int rightLen = m.end(0) - m.end(2);
            set.offset(start, leftLen);
            set.offset(end, rightLen);
            set.put(start, end, SpanSet.SIZE, Integer.valueOf(size));
            gap = m.end(0);
        }
        sb.append(source, gap, source.length());
        source = sb.toString();
    }

    private void findBIUD() {
        int count = 0;
        do {
            count = findbiud();
        } while (count != 0);
    }

    private int findbiud() {
        StringBuilder sb = new StringBuilder(source.length());
        Pattern p = Pattern.compile("\\[(i|b|del|u)\\](.*?)\\[/\\1\\]", Pattern.DOTALL | Pattern.CASE_INSENSITIVE);
        Matcher m = p.matcher(source);
        int gap = 0;
        int count = 0;
        while (m.find()) {
            count++;
            String tag = m.group(1);
            sb.append(source, gap, m.start(0));
            int start = sb.length();
            int leftLen = m.start(2) - m.start(0);
            sb.append(m.group(2));
            int end = sb.length();
            int rightLen = m.end(0) - m.end(2);
            set.offset(start, leftLen);
            set.offset(end, rightLen);
            int flag = -1;
            switch (tag) {
                case "b":
                    flag = SpanSet.BOLD;
                    break;
                case "i":
                    flag = SpanSet.ITATIC;
                    break;
                case "del":
                    flag = SpanSet.DEL;
                    break;
                case "u":
                    flag = SpanSet.UNDER;
                    break;
            }
            set.put(start, end, flag);
            gap = m.end(0);
        }
        sb.append(source, gap, source.length());
        source = sb.toString();
        return count;
    }

    private void findColor() {
        StringBuilder sb = new StringBuilder(source.length());
        Pattern p = Pattern.compile("\\[color=(.*?)\\](.*?)\\[/color\\]", Pattern.DOTALL);
        Matcher m = p.matcher(source);
        int gap = 0;
        while (m.find()) {
            String color = m.group(1);
            sb.append(source, gap, m.start(0));
            int start = sb.length();
            int leftLen = m.start(2) - m.start(0);
            sb.append(m.group(2));
            int end = sb.length();
            int rightLen = m.end(0) - m.end(2);
            set.offset(start, leftLen);
            set.offset(end, rightLen);
            set.put(start, end, SpanSet.COLOR, color);
            gap = m.end(0);
        }
        sb.append(source, gap, source.length());
        source = sb.toString();
    }

    private void findQuote() {
        StringBuilder sb = new StringBuilder(source.length());
        Pattern p = Pattern.compile("\\[quote\\](.*?)\\[/quote\\]", Pattern.DOTALL);
        Matcher m = p.matcher(source);
        int gap = 0;
        while (m.find()) {
            sb.append(source, gap, m.start(0));
            int start = sb.length();
            sb.append(m.group(1));
            int end = sb.length();
            set.put(start, end, SpanSet.QUOTE);
            gap = m.end(0);
        }
        sb.append(source, gap, source.length());
        source = sb.toString();
    }

    private SpannableStringBuilder setSpan(SpanSet set) {
        SpannableStringBuilder sp = SpannableStringBuilder.valueOf(source);
        for (int i = 0; i < set.count; i++) {
            int start = set.starts[i];
            int end = set.ends[i];
            switch (set.flags[i]) {
                case SpanSet.QUOTE:
                    sp.setSpan(new MarginQuoteSpan(), start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
//                    sp.setSpan(new QuoteSpan(Color.BLACK), start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
                    break;
                case SpanSet.COLOR:
                    String color = (String) set.args[i][0];
                    sp.setSpan(new ForegroundColorSpan(Color.parseColor(color)), start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
                    break;
                case SpanSet.BOLD:
                    sp.setSpan(new StyleSpan(Typeface.BOLD), start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
                    break;
                case SpanSet.SIZE:
                    int size = (int) set.args[i][0];
                    float f = size * 1.0f / 100;
                    sp.setSpan(new RelativeSizeSpan(f), start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
                    break;
                case SpanSet.LIST:
                    sp.setSpan(new ListSpan(5), start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
                    break;
                case SpanSet.ALIGN:
                    String align = (String) set.args[i][0];
                    sp.setSpan(new AlignmentSpan.Standard(parseAlign(align)), start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
                    break;
                case SpanSet.IMAGE:
                    final String img = (String) set.args[i][0];
                    final SpanImageSpan imageSpan;
                    sp.setSpan(imageSpan = new AsyncImageSpan(img, sp, new TestCache(DrawableCache.getInstance())), start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
                 /*   new Thread() {
                        @Override
                        public void run() {
                            HttpURLConnection conn = null;
                            try {
                                conn = (HttpURLConnection) new URL(img).openConnection();
                                conn.connect();
                                Bitmap bitmap = BitmapFactory.decodeStream(conn.getInputStream());
                                Drawable drawable1 = new BitmapDrawable(bitmap);
                                drawable1.setBounds(0, 0, bitmap.getWidth(), bitmap.getHeight());
                                imageSpan.setDrawable(drawable1);
                                conn.disconnect();
                                Handler handler = new Handler(Looper.getMainLooper());
                                handler.post(new Runnable() {
                                    @Override
                                    public void run() {
                                        if (callBack != null) {
                                            callBack.complete();
                                        }
                                    }
                                });
                            } catch (IOException e) {
                                e.printStackTrace();
                            } finally {
                                if (conn != null) {
                                    conn.disconnect();
                                }
                            }
                        }
                    }.start();*/
                    break;
                case SpanSet.LINE:
                    sp.setSpan(new LineSpan(Color.parseColor("#a0522d")), start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
                    break;
                case SpanSet.URL:
                    String url = (String) set.args[i][0];
                    sp.setSpan(new UrlClickSpan(url), start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
                    break;
                case SpanSet.ITATIC:
                    sp.setSpan(new StyleSpan(Typeface.ITALIC), start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
                    break;
                case SpanSet.DEL:
                    sp.setSpan(new StrikethroughSpan(), start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
                    break;
                case SpanSet.UNDER:
                    sp.setSpan(new UnderlineSpan(), start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
                    break;
            }
        }
        return sp;
    }

    public static Layout.Alignment parseAlign(String string) {
        switch (string.toLowerCase()) {
            case "center":
                return Layout.Alignment.ALIGN_CENTER;
            case "left":
                return Layout.Alignment.valueOf("ALIGN_LEFT");
            case "right":
                return Layout.Alignment.valueOf("ALIGN_RIGHT");
            default:
                return Layout.Alignment.ALIGN_NORMAL;
        }
    }

    static class TestCache implements ICache {
        DrawableCache cache;

        public TestCache(DrawableCache cache) {
            this.cache = cache;
        }

        @Override
        public void put(String source, Drawable drawable) {
            cache.put(source, drawable);
        }

        @Override
        public Drawable get(String source) {
            return cache.get(source);
        }
    }
}
